/*
JETware Hands-free Extension provides additional features for Windows Mobile HFP support.
Copyright (C) 2004-2009 Jim Thatcher

This program is free software; you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; 
if not, write to the Free Software Foundation, Inc., 
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Source Control header

$Workfile: ObexClient.cpp $
$Revision: 1 $
$Date: 1/1/09 1:11p $
*/
// This file implements an Obex Client for sending files

#include "ObexClient.hpp"
#include "JetBTConfig.h"
#include "bt_sdp.h"

ObexClient::ObexClient()
{
	// m_bState = CONN_STATE_NEW;
	m_pRespBuffer = NULL;
	// might move these to a SyncRequest class
	// m_bReqFinal			= FALSE;
	m_bCmd				= 0;
	m_bResponseCode		= 0;
	m_bResFinal			= FALSE;
	m_wMaxPacketSize	= sizeof(m_bPacketBuffer);
	m_socket			= INVALID_SOCKET;
#ifdef DEBUG
	LogString("ObexClient ctor");
#endif
};

BOOL ObexClient::Put(TCHAR * szFile)
{
#ifdef DEBUG
	CHAR szLog[128];
#endif
	BOOL bRet = FALSE;
	HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return bRet;
	TCHAR * szFilename = wcsrchr(szFile, '\\');
	if (szFilename == NULL)
		szFilename = szFile;
	else
		szFilename++;
	size_t sizeFilename = ((wcslen(szFilename) + 1)) * sizeof(TCHAR);
	// Send filename packet
	OBEX_PUT_REQUEST * pRequest = (OBEX_PUT_REQUEST *) m_bPacketBuffer;
    WORD wPacketLen = sizeFilename + 6;
	pRequest->cmd = OBEX_OPCODE_PUT;
	pRequest->length = htons((USHORT) wPacketLen);
	pRequest->body.type = OBEX_HEADER_NAME;
	pRequest->body.length = htons((USHORT) wPacketLen - 3);
	USHORT * pFileName = (USHORT *) pRequest->body.data;
	for (int n = 0; n < sizeFilename/sizeof(TCHAR); n++)
		*(pFileName+n) = htons((USHORT)szFilename[n]);
#ifdef DEBUG
	LogString("Sending PUT filename packet");
	LogBlob(pRequest, wPacketLen);
#endif
	int nRet = send(m_socket, (char *) pRequest, wPacketLen, 0);
	if (nRet != SOCKET_ERROR)
	{
		nRet = recv(m_socket, (char *) m_bPacketBuffer, sizeof(m_bPacketBuffer), 0);
		if (nRet != SOCKET_ERROR)
		{
			// Send file one packet at a time
			OBEX_BASIC_RESPONSE * pResponse = (OBEX_BASIC_RESPONSE *) m_bPacketBuffer;
			if (pResponse->response & OBEX_REPLY_CONTINUE)
			{
				// now send the file block by block
				DWORD dwBytesLeft = GetFileSize(hFile, NULL);
				while (dwBytesLeft > 0)
				{
					DWORD dwBytesRead = 0;
					// Always need to reset the packet headers because we reuse the same buffer for incoming responses
					pRequest->cmd = OBEX_OPCODE_PUT;
					pRequest->body.type = OBEX_HEADER_BODY;
					BOOL bRead = ReadFile(hFile, pRequest->body.data, MaxPacketSize() - 6, &dwBytesRead, NULL);
					if (!bRead || dwBytesRead < MaxPacketSize() - 6) // assume we are at end of file
					{
						pRequest->cmd |= OBEX_OPCODE_FINALBIT;
						pRequest->body.type = OBEX_HEADER_END_OF_BODY;
					}
					pRequest->length = htons(dwBytesRead + 6);
					pRequest->body.length = htons(dwBytesRead + 3);
					nRet = send(m_socket, (char *) pRequest, dwBytesRead + 6, 0);
					if (nRet != SOCKET_ERROR)
					{
						nRet = recv(m_socket, (char *) pResponse, MaxPacketSize(), 0);
						if ((nRet == SOCKET_ERROR) || (!(pResponse->response & (OBEX_REPLY_CONTINUE | OBEX_REPLY_SUCCESS))))
						{
#ifdef DEBUG
                            LogBlob(pResponse, (nRet > 0) ? nRet : MaxPacketSize());
							sprintf(szLog, "Error with body Response=%i, ws=%i", pResponse->response, WSAGetLastError());
							LogString(szLog);
#endif
							break;
						}
					}
					else
					{
#ifdef DEBUG
						sprintf(szLog, "Error sending body=%i, ws=%i", pResponse->response, WSAGetLastError());
						LogString(szLog);
#endif
						break;
					}
					dwBytesLeft -= dwBytesRead;
				}
				if (dwBytesLeft <= 0)
				{
					bRet = TRUE;
#ifdef DEBUG
					LogString("PUT completed");
#endif
				}
			}
#ifdef DEBUG
			else
			{
				sprintf(szLog, "Response not CONTINUE=%i", pResponse->response);
				LogString(szLog);
			}
#endif
		}
#ifdef DEBUG
		else
		{
			sprintf(szLog, "ObexClient::Put-recv filename failed %i", WSAGetLastError());
			LogString(szLog);
		}
#endif
	}
#ifdef DEBUG
	else
	{
		sprintf(szLog, "ObexClient::Put-send filename failed %i", WSAGetLastError());
		LogString(szLog);
	}
#endif
	CloseHandle(hFile);
	return bRet;
};

void ObexClient::Get()
{
};

void ObexClient::SetPath()
{
};

void ObexClient::Abort()
{
};

void ObexClient::Disconnect()
{
#ifdef DEBUG
	CHAR szLog[128];
#endif
	OBEX_BASIC_REQUEST request;
	request.cmd = OBEX_OPCODE_DISCONNECT;
	request.length = htons(sizeof(request));
#ifdef DEBUG
	LogString("Sending Disconnect request"); 
	// LogBlob(&request, sizeof(request));
#endif
	int nRet = send(m_socket, (char *) &request, sizeof(request), 0);
	if (nRet != SOCKET_ERROR)
	{
		nRet = recv(m_socket, (char *) m_bPacketBuffer, sizeof(m_bPacketBuffer), 0);
		if (nRet != SOCKET_ERROR)
		{
			// parse response
			OBEX_BASIC_RESPONSE * pResponse = (OBEX_BASIC_RESPONSE *) m_bPacketBuffer;
			/*
			if (pResponse->response & OBEX_REPLY_SUCCESS) // Use & instead of == because response may include final bit
			{
				bRet = TRUE;
			}
			else
			*/
			{
#ifdef DEBUG
				sprintf(szLog, "ObexClient::Disconnect-response=%i", pResponse->response);
				LogString(szLog);
				// LogBlob(m_bPacketBuffer, sizeof(OBEX_BASIC_RESPONSE));
#endif
			}
		}
#ifdef DEBUG
		else
		{
			sprintf(szLog, "ObexClient::Disconnect-recv failed error %i", WSAGetLastError());
			LogString(szLog);
		}
#endif
	}
#ifdef DEBUG
	else
	{
		sprintf(szLog, "ObexClient::Disconnect-send failed error %i", WSAGetLastError());
		LogString(szLog);
	}
#endif

	closesocket(m_socket);
	m_socket = INVALID_SOCKET;
	WSACleanup();
};

BOOL ObexClient::Connect(bt_addr ullTarget)
{
#ifdef DEBUG
	CHAR szLog[128];
#endif
	BOOL bRet = FALSE;
	int nRet = 0;
	WSADATA wsa;
	nRet = WSAStartup(WINSOCK_VERSION, &wsa);
#ifdef DEBUG
	sprintf(szLog, "ObexClient::Connect-WSAStartup returned %i - ver %i %i", nRet, wsa.wHighVersion, wsa.wVersion);
	LogString(szLog);
	LogString(wsa.szDescription);
	LogString(wsa.szSystemStatus);
#endif
	if (nRet != 0)
		return bRet;

	m_socket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
#ifdef DEBUG
	sprintf(szLog, "ObexClient::Connect-socket returned %i", m_socket);
	LogString(szLog);
#endif
	if (m_socket != INVALID_SOCKET)
	{
        int nSize = sizeof(SOCKADDR_BTH);
		SOCKADDR_BTH sab;
		int nErr = 0;
		memset (&sab, 0, nSize);
		sab.addressFamily = AF_BTH;
		sab.btAddr = ullTarget;
		sab.serviceClassId = OBEXObjectPushServiceClass_UUID;
		// sab.port = 1;
		// sab.serviceClassId = OBEXFileTransferServiceClass_UUID;
#ifdef DEBUG
		sprintf(szLog, "Attempting to connect to %4.4X %8.8X", GET_NAP(sab.btAddr), GET_SAP(sab.btAddr));
		LogString(szLog);
		LogBlob(&sab, nSize);
#endif
		nRet = connect(m_socket, (sockaddr *) &sab, nSize);
		nErr = WSAGetLastError();
#ifdef DEBUG
		sprintf(szLog, "ObexClient::Connect-connect Error %i, returned %i", nErr, nRet);
		LogString(szLog);
#endif
		if (nRet == SOCKET_ERROR)
		{
#ifdef DEBUG
			sprintf(szLog, "ObexClient::Connect-connect failed error %i", WSAGetLastError());
			LogString(szLog);
#endif
			//TODO: Cleanup
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
			WSACleanup();
			return FALSE;
		}
		OBEX_BASIC_CONNECT_REQUEST request;
		request.cmd = OBEX_OPCODE_CONNECT;
		request.length = htons(sizeof(request));
		request.version = OBEX_VERSION_10;
		request.MaxPacketLen = htons(sizeof(m_bPacketBuffer));
		request.flags = 0;
#ifdef DEBUG
		LogString("Sending CONNECT request");
		// LogBlob(&request, sizeof(request));
#endif
		nRet = send(m_socket, (char *) &request, sizeof(request), 0);
#ifdef DEBUG
		sprintf(szLog, "send returned %i", nRet);
		LogString(szLog);
#endif
		if (nRet != SOCKET_ERROR)
		{
			nRet = recv(m_socket, (char *) m_bPacketBuffer, sizeof(m_bPacketBuffer), 0);
#ifdef DEBUG
			sprintf(szLog, "recv returned %i", nRet);
			LogString(szLog);
#endif
			if (nRet != SOCKET_ERROR)
			{
				// parse response
				OBEX_CONNECT_RESPONSE * pResponse = (OBEX_CONNECT_RESPONSE *) m_bPacketBuffer;
#ifdef DEBUG
				sprintf(szLog, "ObexClient::Connect-response=%i", pResponse->response);
				LogString(szLog);
				LogBlob(m_bPacketBuffer, sizeof(OBEX_CONNECT_RESPONSE)+32);
#endif
				if (pResponse->response & OBEX_REPLY_SUCCESS) // Use & instead of == because response may include final bit
				{
					if (ntohs(pResponse->MaxPacketLen) < m_wMaxPacketSize)
						m_wMaxPacketSize = ntohs(pResponse->MaxPacketLen);
					bRet = TRUE;
				}
				else
				{
#ifdef DEBUG
					sprintf(szLog, "ObexClient::Connect-response not success=%i", pResponse->response);
					LogString(szLog);
#endif
					closesocket(m_socket);
				}
			}
#ifdef DEBUG
			else
			{
				sprintf(szLog, "ObexClient::Connect-recv failed error %i", WSAGetLastError());
				LogString(szLog);
			}
#endif
		}
#ifdef DEBUG
		else
		{
			sprintf(szLog, "ObexClient::Connect-send failed error %i", WSAGetLastError());
			LogString(szLog);
		}
#endif

	}
#ifdef DEBUG
	else
	{
		sprintf(szLog, "ObexClient::Connect-socket failed error %i", GetLastError());
		LogString(szLog);
	}
#endif
/*
	OBEX_CONNECT_REQUEST * pConnectReq = (OBEX_CONNECT_REQUEST *) m_bPacketBuffer;
	m_wMaxResponseSize = ntohs(pConnectReq->MaxPacketLen);
	if (pConnectReq->length > sizeof(OBEX_CONNECT_REQUEST))
	{
		WORD wHdrLen = 0;
		if (pConnectReq->target.type == OBEX_HEADER_TARGET)
		{
			wHdrLen = ntohs(pConnectReq->target.length);
			memset(m_szTarget, 0, sizeof(m_szTarget));
			strncpy(m_szTarget, (char *) pConnectReq->target.data, wHdrLen - 3);
#ifdef DEBUG
			sprintf(szLog, "Connect to target %s", m_szTarget);
			LogString(szLog, DEBUG_OBEX);
#endif
		}
		// Build IRMC-SYNC response, including Connection ID and Who headers
		OBEX_CONNECT_RESPONSE response;
		m_bResponseCode = OBEX_REPLY_SUCCESS;
		m_bResFinal = TRUE;
		response.response = OBEX_REPLY_SUCCESS | OBEX_OPCODE_FINALBIT;
		response.length = htons(sizeof(response));
		response.version = OBEX_VERSION_10;
		response.MaxPacketLen = htons(sizeof(m_bRecvBuffer));
		response.connid.type = OBEX_HEADER_CONNECTION_ID;
		response.connid.value = m_socket;
		response.who.type = OBEX_HEADER_WHO;
		response.who.length = htons(12);
#ifdef DEBUG
		LogBlob(&response, sizeof(response), DEBUG_OBEX);
#endif
		memcpy((char *) response.who.data, (char *) pConnectReq->target.data, 9);
#ifdef DEBUG
		LogString("Responding to CONNECT request", DEBUG_OBEX);
		LogBlob(&response, sizeof(response), DEBUG_OBEX);
#endif
		int nLen = send(m_socket, (char *) &response, sizeof(response), 0);
#ifdef DEBUG
		sprintf(szLog, "send returned %i", nLen);
		LogString(szLog, DEBUG_OBEX);
#endif
	}
	m_bState = CONN_STATE_CONNECTED;
*/
	if (bRet == FALSE)
	{
		if (m_socket != INVALID_SOCKET)
		{
			closesocket(m_socket);
			m_socket = INVALID_SOCKET;
		}
		WSACleanup();
	}
	return bRet;
};
/*
BYTE ObexClient::ParseResponse()
{
#ifdef DEBUG
	CHAR szLog[128];
#endif
	OBEX_BASIC_REQUEST * pReq = (OBEX_BASIC_REQUEST *) m_bRecvBuffer;
	m_bReqFinal = pReq->cmd & OBEX_OPCODE_FINALBIT;
	m_bCmd = pReq->cmd & 0x7F;
#ifdef DEBUG
	sprintf(szLog, "OBEX request %i, final=%i", m_bCmd, m_bReqFinal);
	LogString(szLog, DEBUG_OBEX);
#endif
	return (pReq->cmd);
};
*/